// HTMLParser Library $Name: v1_6 $ - A java-based parser for HTML
// http://sourceforge.org/projects/htmlparser
// Copyright (C) 2004 Somik Raha
//
// Revision Control Information
//
// $Source: /cvsroot/htmlparser/htmlparser/src/org/htmlparser/nodes/AbstractNode.java,v $
// $Author: derrickoswald $
// $Date: 2006/05/31 02:10:15 $
// $Revision: 1.6 $
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

package org.htmlparser.nodes;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.lexer.Page;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.NodeVisitor;

import java.io.Serializable;

/**
 * The concrete base class for all types of nodes (tags, text remarks). This
 * class provides basic functionality to hold the {@link Page}, the starting and
 * ending position in the page, the parent and the list of {@link NodeList
 * children}.
 */
public abstract class AbstractNode implements Node, Serializable {
    /**
     * The page this node came from.
     */
    protected Page mPage;

    /**
     * The beginning position of the tag in the line
     */
    protected int nodeBegin;

    /**
     * The ending position of the tag in the line
     */
    protected int nodeEnd;

    /**
     * The parent of this node.
     */
    protected Node parent;

    /**
     * The children of this node.
     */
    protected NodeList children;

    /**
     * Create an abstract node with the page positions given. Remember the page
     * and start & end cursor positions.
     *
     * @param page  The page this tag was read from.
     * @param start The starting offset of this node within the page.
     * @param end   The ending offset of this node within the page.
     */
    public AbstractNode(Page page, int start, int end) {
        mPage = page;
        nodeBegin = start;
        nodeEnd = end;
        parent = null;
        children = null;
    }

    /**
     * Clone this object. Exposes java.lang.Object clone as a public method.
     *
     * @return A clone of this object.
     * @throws CloneNotSupportedException This shouldn't be thrown since the {@link Node} interface
     *                                    extends Cloneable.
     */
    public Object clone() throws CloneNotSupportedException {
        return (super.clone());
    }

    /**
     * Returns a string representation of the node. It allows a simple string
     * transformation of a web page, regardless of node type.<br>
     * Typical application code (for extracting only the text from a web page)
     * would then be simplified to:<br>
     * <p/>
     * <pre>
     * Node node;
     * for (Enumeration e = parser.elements(); e.hasMoreElements();)
     * {
     * 	node = (Node) e.nextElement();
     * 	System.out.println(node.toPlainTextString());
     * 	// or do whatever processing you wish with the plain text string
     * }
     * </pre>
     *
     * @return The 'browser' content of this node.
     */
    public abstract String toPlainTextString();

    /**
     * Return the HTML for this node. This should be the sequence of characters
     * that were encountered by the parser that caused this node to be created.
     * Where this breaks down is where broken nodes (tags and remarks) have been
     * encountered and fixed. Applications reproducing html can use this method
     * on nodes which are to be used or transferred as they were received or
     * created.
     *
     * @return The sequence of characters that would cause this node to be
     *         returned by the parser or lexer.
     */
    public String toHtml() {
        return (toHtml(false));
    }

    /**
     * Return the HTML for this node. This should be the exact sequence of
     * characters that were encountered by the parser that caused this node to
     * be created. Where this breaks down is where broken nodes (tags and
     * remarks) have been encountered and fixed. Applications reproducing html
     * can use this method on nodes which are to be used or transferred as they
     * were received or created.
     *
     * @param verbatim If <code>true</code> return as close to the original page text
     *                 as possible.
     * @return The (exact) sequence of characters that would cause this node to
     *         be returned by the parser or lexer.
     */
    public abstract String toHtml(boolean verbatim);

    /**
     * Return a string representation of the node. Subclasses must define this
     * method, and this is typically to be used in the manner<br>
     * <p/>
     * <pre>
     * System.out.println(node)
     * </pre>
     *
     * @return A textual representation of the node suitable for debugging
     */
    public abstract String toString();

    /**
     * Collect this node and its child nodes (if-applicable) into the
     * collectionList parameter, provided the node satisfies the filtering
     * criteria.
     * <p/>
     * <p/>
     * This mechanism allows powerful filtering code to be written very easily,
     * without bothering about collection of embedded tags separately. e.g. when
     * we try to get all the links on a page, it is not possible to get it at
     * the top-level, as many tags (like form tags), can contain links embedded
     * in them. We could get the links out by checking if the current node is a
     * {@link org.htmlparser.tags.CompositeTag}, and going through its children.
     * So this method provides a convenient way to do this.
     * <p/>
     * <p/>
     * Using collectInto(), programs get a lot shorter. Now, the code to extract
     * all links from a page would look like:
     * <p/>
     * <pre>
     * NodeList collectionList = new NodeList();
     * NodeFilter filter = new TagNameFilter(&quot;A&quot;);
     * for (NodeIterator e = parser.elements(); e.hasMoreNodes();)
     * 	e.nextNode().collectInto(collectionList, filter);
     * </pre>
     * <p/>
     * Thus, collectionList will hold all the link nodes, irrespective of how
     * deep the links are embedded.
     * <p/>
     * <p/>
     * Another way to accomplish the same objective is:
     * <p/>
     * <pre>
     * NodeList collectionList = new NodeList();
     * NodeFilter filter = new TagClassFilter(LinkTag.class);
     * for (NodeIterator e = parser.elements(); e.hasMoreNodes();)
     * 	e.nextNode().collectInto(collectionList, filter);
     * </pre>
     * <p/>
     * This is slightly less specific because the LinkTag class may be
     * registered for more than one node name, e.g. &lt;LINK&gt; tags too.
     *
     * @param list   The node list to collect acceptable nodes into.
     * @param filter The filter to determine which nodes are retained.
     */
    public void collectInto(NodeList list, NodeFilter filter) {
        if (filter.accept(this)) list.add(this);
    }

    /**
     * Get the page this node came from.
     *
     * @return The page that supplied this node.
     */
    public Page getPage() {
        return (mPage);
    }

    /**
     * Set the page this node came from.
     *
     * @param page The page that supplied this node.
     */
    public void setPage(Page page) {
        mPage = page;
    }

    /**
     * Gets the starting position of the node.
     *
     * @return The start position.
     */
    public int getStartPosition() {
        return (nodeBegin);
    }

    /**
     * Sets the starting position of the node.
     *
     * @param position The new start position.
     */
    public void setStartPosition(int position) {
        nodeBegin = position;
    }

    /**
     * Gets the ending position of the node.
     *
     * @return The end position.
     */
    public int getEndPosition() {
        return (nodeEnd);
    }

    /**
     * Sets the ending position of the node.
     *
     * @param position The new end position.
     */
    public void setEndPosition(int position) {
        nodeEnd = position;
    }

    /**
     * Visit this node.
     *
     * @param visitor The visitor that is visiting this node.
     */
    public abstract void accept(NodeVisitor visitor);

    /**
     * Get the parent of this node. This will always return null when parsing
     * without scanners, i.e. if semantic parsing was not performed. The object
     * returned from this method can be safely cast to a
     * <code>CompositeTag</code>.
     *
     * @return The parent of this node, if it's been set, <code>null</code>
     *         otherwise.
     */
    public Node getParent() {
        return (parent);
    }

    /**
     * Sets the parent of this node.
     *
     * @param node The node that contains this node. Must be a
     *             <code>CompositeTag</code>.
     */
    public void setParent(Node node) {
        parent = node;
    }

    /**
     * Get the children of this node.
     *
     * @return The list of children contained by this node, if it's been set,
     *         <code>null</code> otherwise.
     */
    public NodeList getChildren() {
        return (children);
    }

    /**
     * Set the children of this node.
     *
     * @param children The new list of children this node contains.
     */
    public void setChildren(NodeList children) {
        this.children = children;
    }

    /**
     * Get the first child of this node.
     *
     * @return The first child in the list of children contained by this node,
     *         <code>null</code> otherwise.
     */
    public Node getFirstChild() {
        if (children == null) return null;
        if (children.size() == 0) return null;
        return children.elementAt(0);
    }

    /**
     * Get the last child of this node.
     *
     * @return The last child in the list of children contained by this node,
     *         <code>null</code> otherwise.
     */
    public Node getLastChild() {
        if (children == null) return null;
        int numChildren = children.size();
        if (numChildren == 0) return null;
        return children.elementAt(numChildren - 1);
    }

    /**
     * Get the previous sibling to this node.
     *
     * @return The previous sibling to this node if one exists,
     *         <code>null</code> otherwise.
     */
    public Node getPreviousSibling() {
        Node parentNode = this.getParent();
        if (parentNode == null) // root node
            return null;
        NodeList siblings = parentNode.getChildren();
        if (siblings == null) // this should actually be an error
            return null;
        int numSiblings = siblings.size();
        if (numSiblings < 2) // need at least one other node to have a chance of
            // having any siblings
            return null;
        int positionInParent = -1;
        for (int i = 0; i < numSiblings; i++) {
            if (siblings.elementAt(i) == this) {
                positionInParent = i;
                break;
            }
        }
        if (positionInParent < 1) // no previous siblings
            return null;
        return siblings.elementAt(positionInParent - 1);
    }

    /**
     * Get the next sibling to this node.
     *
     * @return The next sibling to this node if one exists, <code>null</code>
     *         otherwise.
     */
    public Node getNextSibling() {
        Node parentNode = this.getParent();
        if (parentNode == null) // root node
            return null;
        NodeList siblings = parentNode.getChildren();
        if (siblings == null) // this should actually be an error
            return null;
        int numSiblings = siblings.size();
        if (numSiblings < 2) // need at least one other node to have a chance of
            // having any siblings
            return null;
        int positionInParent = -1;
        for (int i = 0; i < numSiblings; i++) {
            if (siblings.elementAt(i) == this) {
                positionInParent = i;
                break;
            }
        }
        if (positionInParent == -1) // this should actually be an error
            return null;
        if (positionInParent == (numSiblings - 1)) // no next sibling
            return null;
        return siblings.elementAt(positionInParent + 1);
    }

    /**
     * Returns the text of the node.
     *
     * @return The text of this node. The default is <code>null</code>.
     */
    public String getText() {
        return null;
    }

    /**
     * Sets the string contents of the node.
     *
     * @param text The new text for the node.
     */
    public void setText(String text) {
    }

    /**
     * Perform the meaning of this tag. The default action is to do nothing.
     *
     * @throws ParserException <em>Not used.</em> Provides for subclasses that may want
     *                         to indicate an exceptional condition.
     */
    public void doSemanticAction() throws ParserException {
    }
}
